

#include "ae.h"

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#include "zmalloc.h"
#include "config.h"

/* 包括该系统支持的最佳复用层。性能依次下降，IO复用的选择，性能依次下降，
 * Linux支持 "ae_epoll.c" 和 "ae_select.c" */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE

#include "ae_kqueue.c"
#include "server.h"

#else
#include "ae_select.c"
#endif
#endif
#endif

/*事件的创建*/
aeEventLoop *aeCreateEventLoop(int setsize) {
    aeEventLoop *eventLoop;
    int i;

    monotonicInit();    /* just in case the calling app didn't initialize */

    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
    /*创建两个尚未初始化的数组*/
    eventLoop->events = zmalloc(sizeof(aeFileEvent) * setsize);
    eventLoop->fired = zmalloc(sizeof(aeFiredEvent) * setsize);
    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
    /*
     * setsize：server.maxclients+CONFIG_FDSET_INCR
     * 1. maxclients代表用户配置的最大连接数，可以在启动的时候通过 --maxclients指定，默认为10000。
     * 2. ONFIG_FDSET_INCR大小为128，给Redis预留一些安全的空间。
     * */
    eventLoop->setsize = setsize;
    eventLoop->timeEventHead = NULL;
    eventLoop->timeEventNextId = 0;
    eventLoop->stop = 0;
    eventLoop->maxfd = -1;
    eventLoop->beforesleep = NULL;
    eventLoop->aftersleep = NULL;
    eventLoop->flags = 0;
    if (aeApiCreate(eventLoop) == -1) goto err;
    /* Events with mask == AE_NONE are not set. So let's initialize the
     * vector with it. */
    for (i = 0; i < setsize; i++)
        eventLoop->events[i].mask = AE_NONE;
    return eventLoop;

    err:
    if (eventLoop) {
        zfree(eventLoop->events);
        zfree(eventLoop->fired);
        zfree(eventLoop);
    }
    return NULL;
}

/* Return the current set size. */
int aeGetSetSize(aeEventLoop *eventLoop) {
    return eventLoop->setsize;
}

/* Tells the next iteration/s of the event processing to set timeout of 0. */
void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
    if (noWait)
        eventLoop->flags |= AE_DONT_WAIT;
    else
        eventLoop->flags &= ~AE_DONT_WAIT;
}

/* Resize the maximum set size of the event loop.
 * If the requested set size is smaller than the current set size, but
 * there is already a file descriptor in use that is >= the requested
 * set size minus one, AE_ERR is returned and the operation is not
 * performed at all.
 *
 * Otherwise AE_OK is returned and the operation is successful. */
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
    int i;

    if (setsize == eventLoop->setsize) return AE_OK;
    if (eventLoop->maxfd >= setsize) return AE_ERR;
    if (aeApiResize(eventLoop, setsize) == -1) return AE_ERR;

    eventLoop->events = zrealloc(eventLoop->events, sizeof(aeFileEvent) * setsize);
    eventLoop->fired = zrealloc(eventLoop->fired, sizeof(aeFiredEvent) * setsize);
    eventLoop->setsize = setsize;

    /* Make sure that if we created new slots, they are initialized with
     * an AE_NONE mask. */
    for (i = eventLoop->maxfd + 1; i < setsize; i++)
        eventLoop->events[i].mask = AE_NONE;
    return AE_OK;
}

void aeDeleteEventLoop(aeEventLoop *eventLoop) {
    aeApiFree(eventLoop);
    zfree(eventLoop->events);
    zfree(eventLoop->fired);

    /* Free the time events list. */
    aeTimeEvent *next_te, *te = eventLoop->timeEventHead;
    while (te) {
        next_te = te->next;
        zfree(te);
        te = next_te;
    }
    zfree(eventLoop);
}

void aeStop(aeEventLoop *eventLoop) {
    eventLoop->stop = 1;
}

/**
 * 其实整个方法就是根据传入的 AE_READABLE 标志将函数 acceptTcpHandler() 赋给了文件事件的读处理指针 rfileProc
 *
 * aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL)
 *
 * aeCreateFileEvent函数接受一个套接字描述符、一个事件类型，以及一个事件处理器作为参数，
 * 将给定套接字的给定事件加入到I/O多路复用程序的监听范围之内，并对事件和事件处理器进行关联;
 * @param eventLoop：事件模型
 * @param fd：文件描述符
 * @param mask：指定注册的事件类型，可以是读或写。
 * @param proc：事件处理函数。
 * @param clientData：客户端数据
 * @return
 */
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
                      aeFileProc *proc, void *clientData) {
    //判断是否超出容量
    if (fd >= eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }
    //获取文件描述符的事件结构体
    aeFileEvent *fe = &eventLoop->events[fd];
    //添加事件，返回-1，添加失败
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    //设置事件值
    fe->mask |= mask;
    //因为我们在上层传入的是AE_READABLE ，所以会走这里的逻辑，
    // 将函数 acceptTcpHandler() 赋给了文件事件的读处理指针 rfileProc
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;

    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) {
    if (fd >= eventLoop->setsize) return;
    aeFileEvent *fe = &eventLoop->events[fd];
    if (fe->mask == AE_NONE) return;

    /* We want to always remove AE_BARRIER if set when AE_WRITABLE
     * is removed. */
    if (mask & AE_WRITABLE) mask |= AE_BARRIER;

    aeApiDelEvent(eventLoop, fd, mask);
    fe->mask = fe->mask & (~mask);
    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
        /* Update the max fd */
        int j;

        for (j = eventLoop->maxfd - 1; j >= 0; j--)
            if (eventLoop->events[j].mask != AE_NONE) break;
        eventLoop->maxfd = j;
    }
}

int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
    if (fd >= eventLoop->setsize) return 0;
    aeFileEvent *fe = &eventLoop->events[fd];

    return fe->mask;
}

long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
                            aeTimeProc *proc, void *clientData,
                            aeEventFinalizerProc *finalizerProc) {
    long long id = eventLoop->timeEventNextId++;
    aeTimeEvent *te;

    te = zmalloc(sizeof(*te));
    if (te == NULL) return AE_ERR;
    te->id = id;
    te->when = getMonotonicUs() + milliseconds * 1000;
    te->timeProc = proc;
    te->finalizerProc = finalizerProc;
    te->clientData = clientData;
    te->prev = NULL;
    te->next = eventLoop->timeEventHead;
    te->refcount = 0;
    if (te->next)
        te->next->prev = te;
    eventLoop->timeEventHead = te;
    return id;
}

int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) {
    aeTimeEvent *te = eventLoop->timeEventHead;
    while (te) {
        if (te->id == id) {
            te->id = AE_DELETED_EVENT_ID;
            return AE_OK;
        }
        te = te->next;
    }
    return AE_ERR; /* NO event with the specified ID found */
}

/* How many milliseconds until the first timer should fire.
 * If there are no timers, -1 is returned.
 *
 * Note that's O(N) since time events are unsorted.
 * Possible optimizations (not needed by Redis so far, but...):
 * 1) Insert the event in order, so that the nearest is just the head.
 *    Much better but still insertion or deletion of timers is O(N).
 * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
 * 寻找第一个快到时间的时间事件
 *
 */

static long msUntilEarliestTimer(aeEventLoop *eventLoop) {
    //时间事件头节点地址
    aeTimeEvent *te = eventLoop->timeEventHead;
    if (te == NULL) return -1;

    aeTimeEvent *earliest = NULL;
    //遍历所有的时间事件
    while (te) {
        //寻找第一个快到时间的时间事件，保存到earliest中。
        if (!earliest || te->when < earliest->when)
            earliest = te;
        te = te->next;
    }

    monotime now = getMonotonicUs();
    //当前时间比较过期时间，返回0就是已经过期，否则就返回还有多久过期
    return (now >= earliest->when) ? 0 : (long) ((earliest->when - now) / 1000);
}

/* 处理时间事件 */
static int processTimeEvents(aeEventLoop *eventLoop) {
    int processed = 0;
    aeTimeEvent *te;
    long long maxId;
    //找到链表的头结点
    te = eventLoop->timeEventHead;
    //当前时间事件表中的最大ID
    maxId = eventLoop->timeEventNextId - 1;
    //获取当前时间
    monotime now = getMonotonicUs();
    //遍历链表
    while (te) {
        long long id;

        /* 如果时间事件已经被删除了 */
        if (te->id == AE_DELETED_EVENT_ID) {
            aeTimeEvent *next = te->next;
            /* 如果refcount=0，跳过当前结点 */
            if (te->refcount) {
                te = next;
                continue;
            }
            //从事件链表中删除事件的结点
            if (te->prev)
                te->prev->next = te->next;
            else
                eventLoop->timeEventHead = te->next;
            if (te->next)
                te->next->prev = te->prev;
            //调用时间事件的终结方法清除该事件
            if (te->finalizerProc) {
                te->finalizerProc(eventLoop, te->clientData);
                now = getMonotonicUs();
            }
            //释放内存
            zfree(te);
            te = next;
            continue;
        }

        /* 确保我们不处理在此迭代中由时间事件创建的时间事件。 请注意，此检查目前无效：
         * 我们总是在头节点添加新的计时器，但是如果我们更改实施细节，则该检查可能会再次有用：
         * 我们将其保留在未来的防御
         * */
        if (te->id > maxId) {
            te = te->next;
            continue;
        }
        //找到已经过期的时间事件
        if (te->when <= now) {
            int retval;

            id = te->id;
            te->refcount++;
            // 调用时间事件处理方法，实际上就是serverCron函数
            retval = te->timeProc(eventLoop, id, te->clientData);
            te->refcount--;
            // 时间事件次数加1
            processed++;
            //重新获取当前时间
            now = getMonotonicUs();
            //如果不是定时事件，则继续设置它的到期时间
            if (retval != AE_NOMORE) {
                te->when = now + retval * 1000;
            } else {
                //此时说明是定时事件，则retval=-1，将其时间事件标记为删除（惰性删除）
                te->id = AE_DELETED_EVENT_ID;
            }
        }
        //更新后继结点指针
        te = te->next;
    }
    //返回执行事件的次数
    return processed;
}

/* 这个方法会处理两种事件，分别是定时触发的事件和Socket I/O触发的读写事件。
 * 因为 redis 是单线程的，这两种事件不可以同时处理，所以该函数中有一种时间分片的策略，
 * 简单来说就是首先计算当前时间距离最近的定时事件触发时的时间差 T，
 * 然后调用 aeApiPoll()设置轮询 Socket 的超时时间为 T，
 * 在超时时间内只处理 Socket 读写事件，超时时间到了后再调用 processTimeEvents() 函数处理定时事件
 *
 * 这个函数最终返回的是处理的事件数。 */
int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
    int processed = 0, numevents;

    /* 如果什么事件都没有，直接返回。 */
    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

    /* Note that we want to call select() even if there are no
     * file events to process as long as we want to process time
     * events, in order to sleep until the next time event is ready
     * to fire. */
    /*当前还没有要处理的文件事件，或者设置了时间事件但是没有设置不阻塞标识*/
    if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
        int j;
        struct timeval tv, *tvp;
        long msUntilTimer = -1;
        //如果设置了时间事件但是没有设置不阻塞标识
        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
            //获取到最早的毫秒计时器（到了时间的）
            msUntilTimer = msUntilEarliestTimer(eventLoop);
        //获取到了最早的毫秒计数器
        if (msUntilTimer >= 0) {
            tv.tv_sec = msUntilTimer / 1000;
            tv.tv_usec = (msUntilTimer % 1000) * 1000;
            //给tvp赋值
            tvp = &tv;
        } else {

            // 没有获取到了最早到时的时间事件，时间事件链表为空

            //设置了时间事件，同时设置了不阻塞标识
            if (flags & AE_DONT_WAIT) {
                // 将tvp的时间设置为0，就不会阻塞
                tv.tv_sec = tv.tv_usec = 0;
                tvp = &tv;
            } else {
                /* 阻塞到第一个时间事件到来 */
                tvp = NULL; /* wait forever */
            }
        }
        //设置了不阻塞，标识，就把tvp的时间设置为0，就不会阻塞
        if (eventLoop->flags & AE_DONT_WAIT) {
            tv.tv_sec = tv.tv_usec = 0;
            tvp = &tv;
        }
        //调用主循环之前的回调函数
        if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)
            eventLoop->beforesleep(eventLoop);

        /*  等待所监听文件描述符上有事件发生。
         * 如果tvp为NULL，则阻塞在此，否则等待tvp设置阻塞的时间，就会有时间事件到时，返回了就绪文件事件的个数
         * */
        numevents = aeApiPoll(eventLoop, tvp);

        /* 调用主循环之后的回调函数 */
        if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
            eventLoop->aftersleep(eventLoop);
        //处理触发的各个事件，包括网络事件和超时事件
        for (j = 0; j < numevents; j++) {
            // 获取就绪文件事件的地址
            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
            // 获取就绪文件事件的类型，文件描述符
            int mask = eventLoop->fired[j].mask;
            int fd = eventLoop->fired[j].fd;
            int fired = 0; /* 初始化当前文件描述符出发的事件数 */

            /* Normally we execute the readable event first, and the writable
             * event later. This is useful as sometimes we may be able
             * to serve the reply of a query immediately after processing the
             * query.
             *
             * However if AE_BARRIER is set in the mask, our application is
             * asking us to do the reverse: never fire the writable event
             * after the readable. In such a case, we invert the calls.
             * This is useful when, for instance, we want to do things
             * in the beforeSleep() hook, like fsyncing a file to disk,
             * before replying to a client. */
            int invert = fe->mask & AE_BARRIER;

            /* 注意“fe->mask & mask & ...”代码：
             * 可能是一个已经处理的事件删除了一个被触发的元素，
             * 我们仍然没有处理，所以我们检查事件是否仍然有效。*/
            if (!invert && fe->mask & mask & AE_READABLE) {
                fe->rfileProc(eventLoop, fd, fe->clientData, mask);
                serverLog(LL_WARNING,"处理完了读事件！！！");
                fired++;
                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */
            }

            /* 文件可写事件发生 */
            if (fe->mask & mask & AE_WRITABLE) {
                // 读写事件的执行方法不同，则执行写事件，避免重复执行相同的方法
                if (!fired || fe->wfileProc != fe->rfileProc) {
                    //调用写事件方法处理读事件
                    fe->wfileProc(eventLoop, fd, fe->clientData, mask);
                    serverLog(LL_WARNING,"处理完了写事件！！！");
                    //当前文件描述符触发的事件数
                    fired++;
                }
            }

            /* 如果我们反转了读写事件的处理顺序，现在在写事件处理结束后触发读事件 */
            if (invert) {
                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */
                // 读写事件的执行发法不同，则执行写事件，避免重复执行相同的方法
                if ((fe->mask & mask & AE_READABLE) &&
                    (!fired || fe->wfileProc != fe->rfileProc)) {
                    //调用读事件方法处理写事件
                    fe->rfileProc(eventLoop, fd, fe->clientData, mask);
                    //当前文件描述符触发的事件数
                    fired++;
                }
            }
            //执行的事件次数+1
            processed++;
        }
    }
    /* 如果存在定时事件，处理时间事件 */
    if (flags & AE_TIME_EVENTS)
        processed += processTimeEvents(eventLoop);

    return processed; /* 返回处理的事件总数 */
}

/* Wait for milliseconds until the given file descriptor becomes
 * writable/readable/exception */
int aeWait(int fd, int mask, long long milliseconds) {
    struct pollfd pfd;
    int retmask = 0, retval;

    memset(&pfd, 0, sizeof(pfd));
    pfd.fd = fd;
    if (mask & AE_READABLE) pfd.events |= POLLIN;
    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;

    if ((retval = poll(&pfd, 1, milliseconds)) == 1) {
        if (pfd.revents & POLLIN) retmask |= AE_READABLE;
        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
        if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
        return retmask;
    } else {
        return retval;
    }
}

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    //当事件组没有停止的时候
    while (!eventLoop->stop) {
        //就会执行这个函数，所以看这个函数的逻辑
        aeProcessEvents(eventLoop, AE_ALL_EVENTS | AE_CALL_BEFORE_SLEEP | AE_CALL_AFTER_SLEEP);

    }
}

char *aeGetApiName(void) {
    return aeApiName();
}

void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
    eventLoop->beforesleep = beforesleep;
}

void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) {
    eventLoop->aftersleep = aftersleep;
}
